# !/usr/bin/env python
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
    File Name：    config_manager.py
    Description:   企业级配置管理工具类，支持多环境、多格式配置，并增强了数据校验
-------------------------------------------------
"""

from __future__ import annotations

import logging
import os
from functools import lru_cache
from pathlib import Path
from typing import Any

import toml
import yaml
from pydantic_settings import BaseSettings
from pydantic import ValidationError
from pydantic import Field
from dotenv import load_dotenv

from apitesting.config.models.conf_models import (
    ProjectConfig,
    MySqlConfig,
    RedisConfig,
    LogConfig,
    EmailConfig,
    DingDingConfig,
    FeiShuConfig,
    WeChatConfig
)

# 加载 .env 文件
load_dotenv()

# 初始化日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# 配置文件基础目录
BASE_DIR = Path(__file__).parent.parent
CONFIG_DIR = BASE_DIR / "config"

# 模块公开接口
__all__ = [
    "settings",
    "config_manager"
]


class AppConfig(BaseSettings):
    project: ProjectConfig = Field(..., description="项目名称")
    mysql: MySqlConfig = Field(..., description="数据库配置")
    redis: RedisConfig = Field(..., description="Redis配置")
    log: LogConfig = Field(..., description="日志配置")
    email: EmailConfig | None = Field(None, description="邮件配置")
    dingding: DingDingConfig | None = Field(None, description="钉钉配置")
    feishu: FeiShuConfig | None = Field(None, description="飞书配置")
    wechat: WeChatConfig | None = Field(None, description="微信配置")

    @classmethod
    def from_dict(cls, config_dict: dict) -> AppConfig:
        """
        从字典中加载配置，返回AppConfig实例
        :param config_dict: 配置字典
        :return: AppConfig实例
        """
        try:
            return cls(**config_dict)
        except ValidationError as e:
            logger.error(f"配置校验失败: {e}")
            raise RuntimeError(f"配置校验失败: {e}")


class ConfigManager:
    """
    配置管理工具类，支持多种配置文件格式并适应企业级的多环境需求。
    """
    BASE_DIR = Path(os.getenv('PROJECT_BASE_DIR', BASE_DIR))
    CONFIG_DIR = CONFIG_DIR
    DEFAULT_ENV = os.getenv('APP_ENV', 'dev')  # 可设置 'dev', 'test', 'prod' 等环境

    CONFIG_TOML_PATH = CONFIG_DIR / 'conf' / f'{DEFAULT_ENV}.toml'
    CONFIG_YAML_PATH = CONFIG_DIR / 'conf' / f'{DEFAULT_ENV}.yaml'

    def __init__(self):
        """
        初始化配置管理器，优先加载 YAML 配置，其次 TOML 配置。
        """
        self._config_data = self._load_config()

    def _load_config(self) -> dict:
        """
        根据优先级加载配置文件，优先加载 YAML，其次 TOML。
        :return: 加载后的配置字典
        """
        config_data = {}
        try:
            if self.CONFIG_YAML_PATH.exists():
                # logger.info(f"加载 YAML 配置来自 {self.CONFIG_YAML_PATH}")
                with open(self.CONFIG_YAML_PATH, 'r', encoding='utf-8') as f:
                    config_data.update(yaml.safe_load(f))
            elif self.CONFIG_TOML_PATH.exists():
                # logger.info(f"加载 TOML 配置来自 {self.CONFIG_TOML_PATH}")
                with open(self.CONFIG_TOML_PATH, 'r', encoding='utf-8') as f:
                    config_data.update(toml.load(f))
            else:
                logger.warning("未找到配置文件。使用默认或环境值.")
        except Exception as e:
            logger.error(f"加载配置文件时发生错误: {e}")
            raise RuntimeError(f"加载配置文件失败: {e}")
        return config_data

    def get(self, key: str, default: Any = None) -> Any:
        """
        获取配置项的值。
        :param key: 配置项的键名
        :param default: 如果键不存在则返回默认值
        :return: 配置项的值
        """
        return getattr(self.app_config, key, default)

    def reload(self):
        """
        重新加载配置，适用于配置文件发生变化的情况。
        """
        logger.info("Reloading configuration...")
        self._config_data = self._load_config()
        self.app_config = AppConfig.from_dict(self._config_data)
        logger.info("Configuration reloaded successfully.")

    @classmethod
    @lru_cache(maxsize=None)
    def instance(cls):
        """
        返回 ConfigManager 的单例实例。
        """
        return cls()


def load_config_file(env: str) -> dict:
    """
    动态加载配置文件，支持 YAML 和 TOML 格式
    :param env: 环境名称（如 dev, prod）
    :return: 解析后的配置字典
    """
    # 配置文件目录
    config_dir = Path(__file__).parent / "conf"

    # 优先加载 .yaml 文件
    toml_path = ConfigManager.CONFIG_TOML_PATH or config_dir / f"{env}.toml"
    yaml_path = ConfigManager.CONFIG_TOML_PATH or config_dir / f"{env}.yaml"

    # 判断文件存在并解析
    if toml_path.exists():
        with open(toml_path, "r", encoding="utf-8") as f:
            return toml.load(f)
    elif yaml_path.exists():
        with open(yaml_path, "r", encoding="utf-8") as f:
            return yaml.safe_load(f)
    else:
        raise FileNotFoundError(f"未找到 '{env}' 环境的配置文件，请检查 {yaml_path} 或 {toml_path}")


def load_config() -> AppConfig:
    """
    加载配置的主入口
    """
    # 从环境变量读取当前环境
    env = os.getenv("APP_ENV", "dev")  # 默认为开发环境

    # 加载 YAML 配置
    yaml_config: dict = load_config_file(env)

    # 合并 YAML 配置和环境变量
    try:
        app_config = AppConfig(**yaml_config)
        return app_config
    except ValidationError as e:
        raise RuntimeError(f"配置校验失败: {e}")


# 初始化配置管理器
settings = load_config()
config_handler = ConfigManager.instance()

# 测试代码
if __name__ == '__main__':
    print(settings.project.name)

    # print(load_config().model_dump())
    # settings.
